#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "map.h"
struct js_hashentry {
    jshashval_t hash;
    struct js_hashentry *next;
    char *key;
    union {
        void    *ptr;
        uint64_t data;
    } v;
};

struct js_hashmap_t {
    int size;
    int prime;
    struct js_hashentry *entry[0/*size*/];
};

static jshashval_t js_hash(const char *key, int prime)
{
  int i, len = strlen(key);
  jshashval_t hash = (1073741824 + 85/*prime number*/) * len;
  for (i = 0; i < len; ++i) {
      hash = (hash<<4)^(hash>>28)^key[i];
  }
  return (hash % prime);
}


#define MAP_EMPTY ((js_hashentry_t*)0x00100100)

int js_hashmap_size(js_hashmap_t *map)
{
  return (map->size);
}

js_hashmap_t *js_hashmap_new(size_t prime)
{
  js_hashmap_t *map;
  size_t i, size = sizeof(js_hashmap_t) + sizeof(js_hashentry_t) * (prime);
  map = (js_hashmap_t*) malloc(size);
  map->prime = prime;
  for (i = 0; i < prime; i++) {
      map->entry[i] = MAP_EMPTY;
  }
  map->size = 0;
  return map;
}

void js_hashmap_set(js_hashmap_t *map, const char *key, void *value)
{
  js_hashentry_t **entry, *e;
  jshashval_t hash = js_hash(key, map->prime);
  assert(map->size >= 0);
  entry = map->entry;
  e = entry[hash];
  while (e != MAP_EMPTY) {
      char *k = e->key;
      if (e->hash == hash && strcmp(k, key) == 0) {
          e->v.ptr = value;
          return;
      }
      e = e->next;
  }
  {
    size_t len = strlen(key);
    e = (js_hashentry_t*) malloc(sizeof(*e));
    e->hash = hash;
    e->key  = (char *) malloc(len + 1);
    strncpy(e->key, key, strlen(key));
    e->key[len] = '\0';
    e->v.ptr = value;
    e->next = entry[hash];
    entry[hash] = e;
  }
  map->size++;
}

void *js_hashmap_get(js_hashmap_t *map, const char *key)
{
  js_hashentry_t **entry, *e;
  jshashval_t hash = js_hash(key, map->prime);
  assert(map->size >= 0);
  entry = map->entry;
  e = entry[hash];
  while (e != MAP_EMPTY) {
      char *k = e->key;
      if (e->hash == hash && strcmp(k, key) == 0) {
          return e->v.ptr;
      }
      e = e->next;
  }
  return NULL;
}

#define JS_ASSERT(msg, expr) {\
    if (!(expr)) {\
        fprintf(stderr, "[%s:%d:%s] '%s' (%s)\n", \
                __FILE__, __LINE__, __func__, msg, #expr);\
        asm volatile("int3");\
    }\
}

void js_hashmap_delete(js_hashmap_t *map)
{
  int i;
  js_hashentry_t **entry, *e;
  JS_ASSERT("map is deleted", map->size >= 0);
  entry = map->entry;
  for (i = 0; i < map->prime; i++) {
      e = entry[i];
      while (e != MAP_EMPTY) {
          js_hashentry_t *old = e;
          char *k = e->key;
          e = e->next;
          free(k);
          free(old);
      }
  }
  memset(map, 0, sizeof(*map));
  map->size = -1;
  free(map);
}

